package main

import (
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"time"
)

func init() {
	rand.Seed(time.Now().Unix())
}

var stop chan struct{}

func main() {
	fmt.Print(Welcome)
	stop = make(chan struct{})
	go startInput()
	go stopSignal()
	<-stop
}

func shuffleCards(cards []string) {
	rand.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] })
}

func startInput() {
	var s string
	if _, err := fmt.Scanf("%s", &s); err != nil {
		stop <- struct{}{}
		return
	}
	switch s {
	case "y", "Y", "yes":
		go start()
	default:
		stop <- struct{}{}
	}
}

func stopSignal() {
	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, os.Kill)
	for {
		<-c
		stop <- struct{}{}
	}
}

func start() {
	cs := &CardSet{Idx: 0}
	cs.Init()
	player := &Player{Chips: 100, Record: 200}
	dealer := &Player{}
	for {
		if ok := checkChips(player); !ok {
			break
		}
		chipIn(player)
		cs.Shuffle()
		player.Cards = make([]string, 0)
		dealer.Cards = make([]string, 0)
		player.Cards = append(player.Cards, cs.Deal())
		dealer.Cards = append(dealer.Cards, cs.Deal())
		player.Cards = append(player.Cards, cs.Deal())
		dealer.Cards = append(dealer.Cards, cs.Deal())
		player.CalcSum()
		dealer.CalcSum()
		if ok := checkBlackJack(player, dealer); ok {
			continue
		}
		fmt.Println(PlayerCards, player.PrintCards())
		fmt.Println(DealerCards, dealer.Cards[0], "**")
		if !play(cs, player) {
			continue
		}
		fmt.Println(DealerCards, dealer.PrintCards())
		if !deal(cs, dealer) {
			player.Chips += player.In
			continue
		}
		fmt.Println(PlayerCards, player.PrintCards())
		fmt.Println(DealerCards, dealer.PrintCards())
		fmt.Printf(ShowSum, player.Sum, dealer.Sum)
		switch {
		case player.Sum > dealer.Sum:
			player.Chips += player.In
			fmt.Println(PlayerWin)
		case player.Sum < dealer.Sum:
			player.Chips -= player.In
			fmt.Println(DealerWin)
		default:
			fmt.Println(Tie)
		}
	}
	stop <- struct{}{}
}

func checkChips(p *Player) bool {
	switch {
	case p.Chips <= 0:
		fmt.Print(Sorry)
		if playAgain() {
			p.Chips, p.Record = 100, 200
			return true
		}
		return false
	case p.Chips >= p.Record:
		fmt.Print(Congratulation)
		if playAgain() {
			p.Record = 2 * p.Record
			return true
		}
		return false
	default:
		return true
	}
}

func playAgain() bool {
	for {
		var s string
		if _, err := fmt.Scanf("%s", &s); err != nil {
			fmt.Println(YNErr)
			continue
		}
		switch s {
		case "y", "Y", "yes":
			return true
		default:
			return false
		}
	}
}

func chipIn(p *Player) {
	in := 0
	for {
		fmt.Printf(ChipIn, p.Chips)
		if _, err := fmt.Scanf("%d", &in); err != nil {
			fmt.Printf(ChipErr, p.Chips)
			continue
		}
		if in > 0 && in <= p.Chips {
			p.In = in
			return
		}
		fmt.Printf(ChipErr, p.Chips)
	}
}

func checkBlackJack(p, d *Player) bool {
	if p.Sum != 21 && d.Sum != 21 {
		return false
	}
	fmt.Println(PlayerCards, p.PrintCards())
	fmt.Println(DealerCards, d.PrintCards())
	switch {
	case p.Sum == 21 && d.Sum == 21:
		fmt.Println(BothBJ)
	case p.Sum == 21:
		p.Chips += p.In
		fmt.Println(PlayerBJ)
	case d.Sum == 21:
		p.Chips -= p.In
		fmt.Println(DealerBJ)
	}
	return true
}

func play(cs *CardSet, p *Player) bool {
	canD := true
	for {
		if canD {
			fmt.Print(Play1)
		} else {
			fmt.Print(Play2)
		}
		var cho int
		if _, err := fmt.Scanf("%d", &cho); err != nil {
			fmt.Println(PlayErr)
			continue
		}
		switch cho {
		case 1:
			if canD {
				canD = false
			}
			p.Cards = append(p.Cards, cs.Deal())
			fmt.Println(PlayerCards, p.PrintCards())
			p.CalcSum()
			if p.Sum > 21 {
				p.Chips -= p.In
				fmt.Println(PlayerBurst)
				return false
			}
			if p.Sum == 21 {
				return true
			}
		case 2:
			return true
		case 3:
			if !canD {
				fmt.Println(PlayErr)
				continue
			}
			p.In += p.In
			fmt.Printf(PlayDou, p.Chips, p.In)
			p.Cards = append(p.Cards, cs.Deal())
			fmt.Println(PlayerCards, p.PrintCards())
			p.CalcSum()
			if p.Sum > 21 {
				p.Chips -= p.In
				fmt.Println(PlayerBurst)
				return false
			}
			return true
		default:
			fmt.Println(PlayErr)
		}
	}
}

func deal(cs *CardSet, p *Player) bool {
	for {
		if p.Sum >= 17 {
			return true
		}
		fmt.Println(Deal)
		p.Cards = append(p.Cards, cs.Deal())
		fmt.Println(DealerCards, p.PrintCards())
		p.CalcSum()
		if p.Sum > 21 {
			fmt.Println(DealerBurst)
			return false
		}
		if p.Sum == 21 {
			return true
		}
		time.Sleep(500 * time.Millisecond)
	}
}
